/* Copyright (c) 2020 liwangqian <liwangqian@huawei.com>, All rights reserved.

   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the
   "Software"), to deal in the Software without restriction, including
   without limitation the rights to use, copy, modify, merge, publish,
   distribute, sublicense, and/or sell copies of the Software, and to
   permit persons to whom the Software is furnished to do so, subject to
   the following conditions:

   The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   --- Optional exception to the license ---

   As an exception, if, as a result of your compiling your source code, portions
   of this Software are embedded into a machine-executable object form of such
   source code, you may redistribute such embedded portions in such object form
   without including the above copyright and permission notices.
*/
#ifndef SIMULINK_BLOCKS_PORT_H
#define SIMULINK_BLOCKS_PORT_H

#include <cstdint>
#include <vector>

#include "simulink.h"
#include "datatype.h"
#include "errors/error_code.h"
#include "utils/shape.h"
#include "utils/noncopyable.h"

namespace Simulink {
namespace Blocks {

struct Channel;

struct Port : public Utils::NonCopyable {
    Port() = default;
    Port(uint32_t id, Utils::Shape2D shape)
        : id_{id}, shape_{shape}
    {}

    virtual ~Port() = default;

    auto GetPortId()        const { return id_;                 }
    auto GetPortShape()     const { return shape_;              }
    auto GetSampleTime()    const { return sampleTime_;         }
    bool IsInheritParent()  const { return inheritFromParent_;  }
    bool IsConnected()      const { return connected_;          }

    void SetPortId(uint32_t id)                 { id_ = id;                  }
    void SetShape(const Utils::Shape2D shape)   { shape_ = shape;            }
    void SetSampleTime(DT::Duration sampleTime) { sampleTime_ = sampleTime;  }

    ErrorCode Send(const DT::Matrix &data)      { return SendImpl(data);     }
    ErrorCode FastSend(const DT::Matrix &data)  { return FastSendImpl(data); }
    ErrorCode Receive(DT::Matrix &data)         { return ReceiveImpl(data);  }
    void      Reset()                           { ResetImpl();               }

    void ConnectTo(Channel &channel)            { ConnectToImpl(channel);      }
    void DisconnectFrom(Channel &channel)       { DisconnectFromImpl(channel); }

protected:
    void SetInheritParent(bool f) { inheritFromParent_ = f; }
    void SetConnected(bool f)     { connected_ = f;         }

    virtual ErrorCode SendImpl(const DT::Matrix &data)     { return ErrorCode::OK; }
    virtual ErrorCode FastSendImpl(const DT::Matrix &data) { return ErrorCode::OK; }
    virtual ErrorCode ReceiveImpl(DT::Matrix &data)        { return ErrorCode::OK; }
    virtual void      ResetImpl()                          {}

    virtual void ConnectToImpl(Channel &channel) = 0;
    virtual void DisconnectFromImpl(Channel &channel) = 0;


private:
    uint32_t id_{0};                //! 端口的序号，比如：1,2,3,4
    Utils::Shape2D shape_;          //! 端口的形状，代表端口可接收/发送的数据的形状，只支持二维矩阵
    DT::Duration sampleTime_{0};    //! 端口采样时间，单位ms
    bool inheritFromParent_{true};  //! 从父对象中继承属性
    bool connected_{false};         //! 端口是否已经链接起来
};

struct OutputPort : public Port {
    ~OutputPort() override;
    OutputPort() = default;
    OutputPort(uint32_t id, Utils::Shape2D shape, const DT::Matrix& out0);

    void SetInitialOutput(const DT::Matrix &out0);

protected:
    ErrorCode SendImpl(const DT::Matrix &data) override;
    ErrorCode FastSendImpl(const DT::Matrix &data) override;

    void ConnectToImpl(Channel &channel) override;
    void DisconnectFromImpl(Channel &channel) override;

    void ResetImpl() override;

private:
    std::vector<Channel*> channels_{};  //! 端口连接的通道，输出端口可以连接到多个通道
    DT::Matrix out0_{};                   //! 端口的初始输出
};

struct InputPort : public Port {
    ~InputPort() override;
    InputPort() = default;
    InputPort(uint32_t id, Utils::Shape2D shape);

protected:
    ErrorCode ReceiveImpl(DT::Matrix &data) override;
    void ConnectToImpl(Channel &channel) override;
    void DisconnectFromImpl(Channel &channel) override;

private:
    Channel *channel_{nullptr}; //! 端口连接的通道，输入端口只能绑定一个通道
};


} // namespace Blocks
} // namespace Simulink


#endif // SIMULINK_BLOCKS_PORT_H